package ide.modelo;

import ide.vista.VentanaConsola;

import java.util.ArrayList;

import maquina.ByteString;
import maquina.UnidadDeControl;
import maquina.excepciones.CadenaNoAptaComoByteException;
import maquina.excepciones.DireccionFueraDeRangoException;
import maquina.excepciones.ProgramaNoSoportadoException;

/* Clase Singleton */
public class Ejecutor {

	private static Ejecutor instanciaUnica;
	private UnidadDeControl uDeControl;
	private Pipeline pipeline;

	public static Ejecutor getInstancia() {
		if (instanciaUnica == null) {
			instanciaUnica = new Ejecutor();
		}
		return instanciaUnica;
	}

	private Ejecutor() {
		uDeControl = UnidadDeControl.getInstancia();
		uDeControl.getModuloES().setDispositivoES(new VentanaConsola());
		pipeline = new Pipeline();
	}

	public void cargarRAM(ArrayList<String> instrucciones) throws ProgramaNoSoportadoException {
		/*
		 * recorrer toda la lista de instrucciones y colocar cada byte de la instruccion en una celda distinta(2 bytes
		 * por instruccion, 2 celdas contiguas). uDeControl.getRAM().almacenarEn(valor, celda);
		 */
		
		if ( instrucciones.size() > uDeControl.getMaximoDeInstruccionesSoportadas() ){
			throw new ProgramaNoSoportadoException();
		}

		String linea = "";
		int j = 0;
		int k = 0;

		for (int i = 0; i < instrucciones.size(); i++) {
			linea = instrucciones.get(i);
			try {
				j = i * 2;
				k = (i * 2) + 1;
				ByteString primeraParteInstruccion = new ByteString(linea.substring(0, 8));
				ByteString segundaParteInstruccion = new ByteString(linea.substring(8));
				uDeControl.getRAM().almacenarEn(primeraParteInstruccion, j);
				uDeControl.getRAM().almacenarEn(segundaParteInstruccion, k);
			} catch (DireccionFueraDeRangoException | CadenaNoAptaComoByteException e) {

			} finally {

			}

		}
		//seteo el registro valla
		int ultimaCeldaOcupadaPorInstrucciones = instrucciones.size() * 2 - 1;
		uDeControl.getRegistros().setRegistroValla(ultimaCeldaOcupadaPorInstrucciones);
	}

	public ArrayList<String> leerRAM() {
		ArrayList<String> lista = new ArrayList<String>();
		for (int i = 0; i < uDeControl.getRAM().CANTCELDAS; i++) {
			try {
				lista.add(uDeControl.getRAM().leer(i).aStringHexa());
			} catch (DireccionFueraDeRangoException | CadenaNoAptaComoByteException e) {
				e.printStackTrace();
			}
		}
		return lista;
	}

	public void ejecutarDeFormaDirecta() throws RuntimeException {
		try {
			uDeControl.iniciarEjecucionDirecta();
		} catch (Exception e) {
			throw new RuntimeException(e.toString());
		}

	}

	public void ejecutarSiguienteInstruccion() throws RuntimeException {
		try {
			uDeControl.ejecutarCicloDeInstruccion();
		} catch (Exception e) {
			// TODO hay que hacer un catch por cada tipo de excepcion
			throw new RuntimeException(e.toString());
		}
	}

	public void ejecutarPasoAPaso(ArrayList<String> lineas) throws RuntimeException {
		try {
			uDeControl.iniciarEjecucionPasoAPaso();
		} catch (Exception e) {
			throw new RuntimeException(e.toString());
		}
		pipeline.setLineasEditorParseadas(lineas);
	}

	public boolean finDeProgramaAlcanzado() {
		return uDeControl.getFinDeProgramaAlcanzado();
	}

	public String getInstruccionActual() {
		return pipeline.getInstruccion(uDeControl.getRegistros().getProgramCounter());
	}

	public ArrayList<String> getProximasInstrucciones() {
		return pipeline.getProximasInstrucciones(uDeControl.getRegistros().getProgramCounter());
	}

}
